package com.ruoyi.eqmonitor.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateRange;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.eqmonitor.dispose.domain.OpentsdbTags;
import com.ruoyi.eqmonitor.dispose.dtos.OpentsdbTagsData;
import com.ruoyi.eqmonitor.dispose.service.IotService;
import com.ruoyi.eqmonitor.domain.OptDeviceMonitorAnalyseData;
import com.ruoyi.eqmonitor.domain.SnCodeRelation;
import com.ruoyi.eqmonitor.dtos.OptDeviceDto;
import com.ruoyi.eqmonitor.dtos.OptDeviceHistoryDto;
import com.ruoyi.eqmonitor.exception.OpentsdbException;
import com.ruoyi.eqmonitor.exception.OpentsdbQueryException;
import com.ruoyi.eqmonitor.mapper.OptDeviceMonitorAnalyseDataMapper;
import com.ruoyi.eqmonitor.service.IOptDeviceMonitorAnalyseDataService;
import com.ruoyi.eqmonitor.service.IOptDeviceMonitorDataService;
import com.ruoyi.eqmonitor.service.IOptDeviceService;
import com.ruoyi.eqmonitor.service.ISnCodeRelationService;
import com.ruoyi.eqmonitor.utils.Constant;

import com.ruoyi.eqmonitor.utils.OpenTSDBUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.nio.reactor.IOReactorException;
import org.opentsdb.client.OpenTSDBClient;
import org.opentsdb.client.bean.request.Query;
import org.opentsdb.client.bean.request.SubQuery;
import org.opentsdb.client.bean.response.QueryResult;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Collectors;

@Slf4j
@Service
public class OptDeviceMonitorDataServiceImpl implements IOptDeviceMonitorDataService {
    @Resource
    private IOptDeviceMonitorAnalyseDataService iOptDeviceMonitorAnalyseDataService;
    @Resource
    private OpenTSDBUtils openTSDBUtils;
    @Resource
    private IOptDeviceService iOptDeviceService;
    @Resource
    private OptDeviceMonitorAnalyseDataMapper deviceMonitorAnalyseDataMapper;

    @Override
    public List<Map<String, Object>> sewagePumpMonitorInfo(List<Long> pumpIds) {
        // 没有泵站传入 则直接返回空
        if (CollectionUtil.isEmpty(pumpIds)) {
            return ListUtil.empty();
        }
        List<String> deviceTypes = Arrays.stream(Constant.EquipmentType.values()).filter(equipmentType -> {
            // 不查井盖
            return !Constant.EquipmentType.SJ300_N_D2.equals(equipmentType);
        }).map(Constant.EquipmentType::getCode).distinct().collect(Collectors.toList());
        OptDeviceDto queryDto = new OptDeviceDto();
        queryDto.setDeviceTypes(deviceTypes);
        queryDto.setPumpStationIds(pumpIds);
        List<OptDeviceDto> optDeviceDtos = iOptDeviceService.selectOptDeviceList(queryDto);
        if (CollectionUtil.isEmpty(optDeviceDtos)) {
            return ListUtil.empty();
        }
        // 设备按照设备编码分组
        Map<String, OptDeviceDto> deviceMap = optDeviceDtos.stream().collect(Collectors.toMap(OptDeviceDto::getDeviceCode, Function.identity()));
        // 监测指标进行分组
        Map<String, Constant.MonitorInfo> monitorMap = ListUtil.of(
                Constant.MonitorInfo.M_0x05D1, // 液位
                Constant.MonitorInfo.M_0x0592, // 流量
                Constant.MonitorInfo.M_0x0527, // COD
                Constant.MonitorInfo.M_0x0528 // 氨氮
        ).stream().collect(Collectors.toMap(Constant.MonitorInfo::getCode, Function.identity()));
        // 取到所有设备的设备编码
        String[] deviceCodes = optDeviceDtos.stream().map(OptDeviceDto::getDeviceCode).distinct().toArray(String[]::new);
        // 向前推1小时
        Date current = DateUtil.offsetHour(new Date(), -1);
        DateTime yestoday = DateUtil.offsetDay(current, -1);
        DateTime start = DateUtil.offsetDay(current, -3);
        SubQuery.Filter snCodeFilter = OpenTSDBUtils.createSnCodeFilter(deviceCodes);
        SubQuery.Filter codFilter = OpenTSDBUtils.createMonitorCodeFilter(Constant.MonitorInfo.M_0x0527.getCode(),Constant.MonitorInfo.M_0x0528.getCode());
        SubQuery.Filter liuliangFilter = OpenTSDBUtils.createMonitorCodeFilter(Constant.MonitorInfo.M_0x0592.getCode());
        SubQuery.Filter yeweiFilter = OpenTSDBUtils.createMonitorCodeFilter(Constant.MonitorInfo.M_0x05D1.getCode());

        OpenTSDBClient queryClient = null;
        try {
            // 5.1 创建连接
            queryClient = openTSDBUtils.readClient();
            List<QueryResult> queryResults = new ArrayList<>();
            //当前时间前推73小时,每6小时取样一次
            /*List<QueryResult> queryResults = openTSDBUtils.queryByOpentsdb(
                    queryClient,
                    start,
                    current,
                    SubQuery.Aggregator.NONE,
                    "6hc-avg-zero",
                    snCodeFilter,
                    codFilter
            );
            //当前时间前推25小时,每1小时取样一次流量
            queryResults.addAll(openTSDBUtils.queryByOpentsdb(
                    queryClient,
                    yestoday,
                    current,
                    SubQuery.Aggregator.NONE,
                    "1hc-avg-zero",
                    snCodeFilter,
                    liuliangFilter
            ));
            //当前时间前推73小时,每1小时取样一次流量
            queryResults.addAll(openTSDBUtils.queryByOpentsdb(
                    queryClient,
                    start,
                    current,
                    SubQuery.Aggregator.NONE,
                    "1hc-avg-zero",
                    snCodeFilter,
                    yeweiFilter
            ));*/
            // 聚合 按照 监测指标 -> 设备编码 -> 时序数据 进行处理
            Map<String, Map<String, OpentsdbTagsData>> groupCollect = queryResults
                    .stream()
                    // 转为OpentsdbTagsData
                    .map(queryResult -> new OpentsdbTagsData(OpenTSDBUtils.decodeToBean(queryResult.getTags()), queryResult.getDps()))
                    .collect(
                            // 按照监测指标进行分组
                            Collectors.groupingBy(tagData -> {
                                        return tagData.getOpentsdbTags().getMonitorCode();
                                    },
                                    // 按照设备编码进行分组
                                    Collectors.groupingBy(tagData -> {
                                                return tagData.getOpentsdbTags().getSnCode();
                                            },
                                            // 新构建Collector，将每组的数据汇集到一起
                                            Collector.of(OpentsdbTagsData::new, (o, p) -> {
                                                LinkedHashMap<Long, Number> tempMap = new LinkedHashMap<>();
                                                tempMap.putAll(MapUtil.emptyIfNull(o.getDps()));
                                                tempMap.putAll(MapUtil.emptyIfNull(p.getDps()));
                                                o.setDps(tempMap);
                                                o.setOpentsdbTags(p.getOpentsdbTags());
                                            }, (o, p) -> o)
                                    )
                            )
                    );
            List<Map<String, Object>> res = new ArrayList<>();
            // 保证每组监测指标都存在
            for (Constant.MonitorInfo monitorInfo : monitorMap.values()) {
                Map<String, Object> monitorResMap = new HashMap<>();
                List<Map<String, Object>> pumpResList = new ArrayList<>();
                // 取到这个监测指标所有设备的数据
                if(!groupCollect.containsKey(monitorInfo.getCode())) {
                    monitorResMap.put("monitorCode", monitorInfo.getCode());
                    monitorResMap.put("monitorName", monitorInfo.getName());
                    monitorResMap.put("unit", monitorInfo.getUnit());
                    monitorResMap.put("pumpDataList", pumpResList);
                    res.add(monitorResMap);
                	continue;
                }
                Map<String, OpentsdbTagsData> snCodeMap = groupCollect.get(monitorInfo.getCode());
                snCodeMap.forEach((deviceCode, opentsdbTagsData) -> {
                    List<Map<String, Object>> dpsResList = new ArrayList<>();
                    OptDeviceDto device = deviceMap.get(deviceCode);
                    // 排序试试
                    OpentsdbTags opentsdbTags = opentsdbTagsData.getOpentsdbTags();
                    TreeMap<Long, Number> treeMap = new TreeMap<>(opentsdbTagsData.getDps());

                    for (Map.Entry<Long, Number> entry : treeMap.entrySet()) {
                        Map<String, Object> temp = new HashMap<>();
                        temp.put("date", OpenTSDBUtils.deserializeDate(entry.getKey()));
                        temp.put("time", OpenTSDBUtils.deserializeTime(entry.getKey()));
                        double val = NumberUtil.round(NumberUtil.toBigDecimal(entry.getValue()), 3).doubleValue();
                        // 处理一下液位高度的容错 统一改为m`
                        if (val > 7 && Constant.MonitorInfo.M_0x05D1.equals(monitorInfo)) {
                            temp.put("value", NumberUtil.div(val, 100D, 3));
                            temp.put("unit", monitorInfo.getUnit());
                        } else {
                            temp.put("value", val);
                            temp.put("unit", StrUtil.nullToDefault(opentsdbTags.getDataUnit(), monitorInfo.getUnit()));
                        }
                        dpsResList.add(temp);
                    }
                    Map<String, Object> pumpResMap = new HashMap<>(BeanUtil.beanToMap(device));
                    pumpResMap.put("monitorDataList", dpsResList);
                    pumpResList.add(pumpResMap);
                });
                monitorResMap.put("monitorCode", monitorInfo.getCode());
                monitorResMap.put("monitorName", monitorInfo.getName());
                monitorResMap.put("unit", monitorInfo.getUnit());
                monitorResMap.put("pumpDataList", pumpResList);
                res.add(monitorResMap);
            }
            return res;
        } catch (IOReactorException e) {
            log.error("创建opentsd连接失败", e);
        } catch (Exception e) {
            log.error("查询opentsdb失败", e);
        } finally {
            openTSDBUtils.closeClient(queryClient);
        }
        return ListUtil.empty();
    }

	@Override
    public List<Map<String, Object>> sewagePumpCumulativeDischarge(List<Long> pumpStationIds) {
        if (CollectionUtil.isEmpty(pumpStationIds)) return ListUtil.empty();
        // 查询设备
        Constant.MonitorInfo monitorInfo = Constant.MonitorInfo.C_CC0001;
        OptDeviceDto queryDto = new OptDeviceDto();
        queryDto.setDeviceType(Constant.EquipmentType.SR500_L_Y0_LD.getCode());
        queryDto.setPumpStationIds(pumpStationIds);
        List<OptDeviceDto> optDeviceDtos = iOptDeviceService.selectOptDeviceList(queryDto);
        // 过滤设备编码
        List<String> deviceCodes = optDeviceDtos.stream().map(OptDeviceDto::getDeviceCode).distinct().collect(Collectors.toList());
        if (CollectionUtil.isEmpty(deviceCodes)) return ListUtil.empty();

        // 查询昨日与前日的泵站累计流量数据
        LambdaQueryWrapper<OptDeviceMonitorAnalyseData> wrappers = Wrappers.lambdaQuery(OptDeviceMonitorAnalyseData.class);
        wrappers.in(OptDeviceMonitorAnalyseData::getDeviceCode, deviceCodes);
        wrappers.eq(OptDeviceMonitorAnalyseData::getCode, monitorInfo.getCode());
        Date today = DateUtil.beginOfDay(new Date());
        DateTime start = DateUtil.offsetDay(today, -2);
        DateTime end = DateUtil.offsetDay(today, -1);
        wrappers.between(OptDeviceMonitorAnalyseData::getMonitorTime, start, today);
        List<OptDeviceMonitorAnalyseData> analyseList = iOptDeviceMonitorAnalyseDataService.list(wrappers);

        // 组装拼接并返回
        List<Map<String, Object>> rtn = optDeviceDtos.stream().map(item -> {
			Map<String, Object> map = BeanUtil.beanToMap(item);

			// 昨日
			OptDeviceMonitorAnalyseData yesterday = CollectionUtil.findOne(analyseList, data -> {
				return StrUtil.equals(item.getDeviceCode(), data.getDeviceCode())
						&& DateUtil.isSameDay(data.getMonitorTime(), end);
			});
			// 前日
			OptDeviceMonitorAnalyseData beforeYesterday = CollectionUtil.findOne(analyseList, data -> {
				return StrUtil.equals(item.getDeviceCode(), data.getDeviceCode())
						&& DateUtil.isSameDay(data.getMonitorTime(), start);
			});
			map.put("unit", monitorInfo.getUnit());
			map.put("monitorName", monitorInfo.getName());
			map.put("yesterday", yesterday == null ? null : yesterday.getCodeValue());
			map.put("beforeYesterday", beforeYesterday == null ? null : beforeYesterday.getCodeValue());
			return map;
		}).collect(Collectors.toList());


    	//查询所有设备类型的正向累积流量(昨天的和今天的)
        SubQuery.Filter snCodeFilter = OpenTSDBUtils.createSnCodeFilter(deviceCodes.toArray(new String[0]));
        SubQuery.Filter monitorCodeFilter = OpenTSDBUtils.createMonitorCodeFilter("0x0590");
        List<QueryResult> queryResults = null;
        /*try {
            OpenTSDBClient queryClient = openTSDBUtils.readClient();
        	queryResults = openTSDBUtils.queryByOpentsdb(
                    queryClient,
                    DateUtil.offsetDay(today, -1),
                    new Date(),
                    SubQuery.Aggregator.NONE,
                    null,
                    //null,
                    snCodeFilter,
                    monitorCodeFilter
            );
		} catch (Exception e) {
			e.printStackTrace();
		}*/

        //opentsdb查询报错
        if(null == queryResults) {
        	for (int i = 0; i < rtn.size(); i++) {
        		rtn.get(i).put("today", new BigDecimal(0));
        	}
        	return rtn;
        }

        for (int i = 0; i < rtn.size(); i++) {
        	try {
            	Map<String,Object> device = rtn.get(i);
        		String deviceCode = device.get("deviceCode").toString();
        		QueryResult result = null;
            	for (int j = 0; j < queryResults.size(); j++) {
            		if(deviceCode.equalsIgnoreCase(queryResults.get(j).getTags().get("snCode"))) {
            			result = queryResults.get(j);
                    	break;
                	}
            	}
            	if(null == result) {
            		device.put("today", new BigDecimal(0));
            		continue;
            	}
                //开始正向累积流量(昨天的最后一个值)
                BigDecimal start_flow = null;
                Long start_key = null;
                Long end_key = null;
                BigDecimal end_flow = null;
                for (Long key : result.getDps().keySet()) {
                	if(null == start_flow) {
                    	if((key * 1000L) >= today.getTime()) {
                    		start_flow = NumberUtil.toBigDecimal(result.getDps().get(start_key));
                    	}else {
                    		start_key = key;
                    	}
                	}
                	end_key = key;
    			}
                end_flow = NumberUtil.toBigDecimal(result.getDps().get(end_key));
                if(null == start_flow || null == end_flow) {
                	device.put("today", new BigDecimal(0));
                }else {
                    device.put("today", NumberUtil.round(NumberUtil.toBigDecimal(end_flow.doubleValue() - start_flow.doubleValue()), 3));
                }
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
        //流量排序
        rtn.sort(new Comparator<Object>() {
			@SuppressWarnings("unchecked")
			@Override
			public int compare(Object o1, Object o2) {
				Map<String,Object> m1 = (Map<String, Object>) o1;
				Map<String,Object> m2 = (Map<String, Object>) o2;
				BigDecimal t1 = (BigDecimal) m1.get("today");
				BigDecimal t2 = (BigDecimal) m2.get("today");
				return t2.compareTo(t1);
			}});
        return rtn;
    }

	/**
	 * type:
	 * 	'normal': 正常查询两个月的
	 * 	'year-to-year' : 同比
	 *  'month-to-month' : 环比
	 */
    @Override
    public List<Map<String, Object>> deviceMonitorChartAnalyse(String deviceCode,String type) {
        OptDeviceDto device = iOptDeviceService.getDeviceByCode(deviceCode);
        if (device == null) {
            return ListUtil.empty();
        }
        Constant.MonitorInfo[] monitorsByType = Constant.EquipmentTypeMonitorInfo.getMonitorsByType(device.getDeviceType());
        if (ArrayUtil.isEmpty(monitorsByType)) {
            return ListUtil.empty();
        }
        // 过滤自定义分析属性
        monitorsByType = Arrays.stream(monitorsByType).filter(monitorInfo ->
                Constant.MqttMsgType.CUSTOM.equals(monitorInfo.getParameterType())
        ).toArray(Constant.MonitorInfo[]::new);
        if (ArrayUtil.isEmpty(monitorsByType)) {
            return ListUtil.empty();
        }

        // 查询此设备所有的阈值,液位阈值默认井深的70%
        List<Map<String,Object>> thresholds = deviceMonitorAnalyseDataMapper.getDeviceMonitorConfigByCode(deviceCode);
        // 最近2个月
        Date yesterday = DateUtil.offsetDay(new Date(), -1);
        Date start = DateUtil.beginOfDay(DateUtil.offsetMonth(yesterday, -2));


        // 监测指标
        List<String> monitorCode = Arrays.stream(monitorsByType).map(Constant.MonitorInfo::getCode).distinct().collect(Collectors.toList());
        LambdaQueryWrapper<OptDeviceMonitorAnalyseData> wrapper = Wrappers.lambdaQuery();
        // 设备编码
        wrapper.eq(OptDeviceMonitorAnalyseData::getDeviceCode, device.getDeviceCode());

        // 日期检索
        if(type.equalsIgnoreCase("year-to-year")) {
        	yesterday = DateUtil.offsetMonth(yesterday, -12);
            start = DateUtil.offsetMonth(start, -12);
        }else if(type.equalsIgnoreCase("month-to-month")) {
        	yesterday = DateUtil.offsetMonth(yesterday, -2);
        	start = DateUtil.offsetMonth(start, -2);
        }
        wrapper.between(OptDeviceMonitorAnalyseData::getMonitorTime, start, yesterday);

        // 监测指标
        wrapper.in(OptDeviceMonitorAnalyseData::getCode, monitorCode);

        List<OptDeviceMonitorAnalyseData> list = iOptDeviceMonitorAnalyseDataService.list(wrapper);
        List<DateTime> dateTimes = DateUtil.rangeToList(start, yesterday, DateField.DAY_OF_MONTH);
        // 按照监测指标进行分组 分组后按照日期再进行分组
        Map<String, Map<String, List<OptDeviceMonitorAnalyseData>>> collectMonitorList = list.stream()
                .collect(Collectors.groupingBy(
                        OptDeviceMonitorAnalyseData::getCode,
                        Collectors.groupingBy(
                                dataInfo2 -> DateUtil.formatDate(dataInfo2.getMonitorTime())
                        )
                ));
        List<Map<String, Object>> resultList = new ArrayList<>();
        for (Constant.MonitorInfo info : monitorsByType) {
            Map<String, List<OptDeviceMonitorAnalyseData>> monitorCodeGroup = collectMonitorList.get(info.getCode());
            Map<String, Object> resultMap = new HashMap<>();
            List<OptDeviceMonitorAnalyseData> infos = new ArrayList<>();
            for (int i = 0; i < thresholds.size(); i++) {
            	//液位高度日均值
				if(Constant.MonitorInfo.C_CC0007.equals(info) &&
						thresholds.get(i).get("alarm_code").toString().equalsIgnoreCase(Constant.MonitorInfo.M_0x05D1.getCode())) {
					resultMap.put("alarmValue", thresholds.get(i).get("alarm_value"));
				}
				//COD日均值
				if(Constant.MonitorInfo.C_CC0002.equals(info) &&
						thresholds.get(i).get("alarm_code").toString().equalsIgnoreCase(Constant.MonitorInfo.M_0x0527.getCode())) {
					resultMap.put("upperLimit", thresholds.get(i).get("upper_limit"));
					resultMap.put("floorLimit", thresholds.get(i).get("floor_limit"));
				}
				//氨氮日均值
				if(Constant.MonitorInfo.C_CC0003.equals(info) &&
						thresholds.get(i).get("alarm_code").toString().equalsIgnoreCase(Constant.MonitorInfo.M_0x0528.getCode())) {
					resultMap.put("upperLimit", thresholds.get(i).get("upper_limit"));
					resultMap.put("floorLimit", thresholds.get(i).get("floor_limit"));
				}
			}
            resultMap.put("code", info.getCode());
            resultMap.put("name", info.getName());
            resultMap.put("unit", info.getUnit());
            for (DateTime dateTime : dateTimes) {
                List<OptDeviceMonitorAnalyseData> monitorAnalyseData = null;
                if (monitorCodeGroup != null) {
                    monitorAnalyseData = monitorCodeGroup.get(dateTime.toDateStr());
                }
                OptDeviceMonitorAnalyseData last = CollectionUtil.getLast(monitorAnalyseData);
                if (last == null) {
                    last = new OptDeviceMonitorAnalyseData();
                    last.setAbx(device.getAbx());
                    last.setAby(device.getAby());
                    last.setMonitorName(info.getName());
                    last.setCode(info.getCode());
                    last.setSiteName(device.getSiteName());
                    last.setCodeValue(0D);
                    last.setDeviceCode(device.getDeviceCode());
                    last.setDeviceName(device.getDeviceName());
                    last.setDeviceType(device.getDeviceType());
                    last.setMonitorTime(dateTime);
                }
                last.setDataUnit(info.getUnit());
                infos.add(last);
            }
            resultMap.put("infos", infos);
            resultList.add(resultMap);
        }
        return resultList;
    }
    @Resource
    private ISnCodeRelationService snCodeRelationService;
    /**
     * 查询降精度的历史数据
     * @param historyDto 查询参数
     * @return 降精度的历史数据结果集
     * 格式: {
     *     设备编码(931JSZ): {
     *         时间(2022-10-11 11:11 || 2022-11-01 || 2022-11-02 11): {
     *              value: 数据值
     *              unit: 单位
     *         }
     *     }
     * }
     *  "1U30CX": [
     *      {
     *         "date": "2022-12-17",
     *         "time": "00:00",
     *          "unit": "mg/L",
     *          "value": 0.0
     *      },
     *      ...
     *   ]
     *
     */
    @Override
    public Map<String, List<Map<String, Object>>> getDownsampleHistory(OptDeviceHistoryDto historyDto) {
        Map<String, List<Map<String, Object>>> mapping;
        Map<String, List<Map<String, Object>>> mapping2 = new HashMap<>();
        Map<String, List<Map<String, Object>>> targetMapping = new HashMap<>();
        // 判断是否为分析类数据
        if (Constant.MonitorInfo.getCustomInfoByCode(historyDto.getMonitorCode()) != null) {
            mapping = getCustomHistory(historyDto);
        } else {
            mapping = queryDownsampleByIotdb(historyDto);
            log.info("mapping:{}", JSONObject.toJSONString(mapping));
            //是否是需要转化的24小时雨量数据
            if("0x0566".equals(historyDto.getMonitorCode())){
                List<String> snCodes = new ArrayList<>();
                snCodes.add("801SPA");
                snCodes.add("024RLV");
                snCodes.add("FPJ439");

                snCodes.add("692TFK");
                snCodes.add("S142VU");
                snCodes.add("S045QP");
                snCodes.add("KP0E90");
                snCodes.add("01S1UG");
                historyDto.setDeviceCodes(snCodes);
                targetMapping = queryDownsampleByIotdb(historyDto);
                log.info("targetMapping:{}", JSONObject.toJSONString(targetMapping));
            }
        }
        if("0x0566".equals(historyDto.getMonitorCode())){
            Set<String> keys = mapping.keySet();
            for (String key:
                    keys) {
                SnCodeRelation snCodeRelation = snCodeRelationService.getBySnCode(key);
                if(snCodeRelation!=null && !snCodeRelation.getSnCode().equals(snCodeRelation.getSnArea())){
                    String targetCode = snCodeRelation.getSnArea();
                    //数值修正
                    List<Map<String, Object>> targetList = new ArrayList<>();;
                    List<Map<String, Object>> targetList2 = new ArrayList<>();
                    log.info("targetMapping:{}", JSONObject.toJSONString(targetMapping));
                    targetList = targetMapping.get(targetCode);;
                    log.info("--------key--------:{}", key);
                    log.info("targetList:{}", JSONObject.toJSONString(targetList));
                    if(targetList !=null){
                        for (Map<String, Object> map:
                             targetList) {
                            Map<String, Object> map2 = new HashMap<>();
                            double d = Double.parseDouble(map.get("value").toString());
                            map2.put("date", map.get("date"));
                            map2.put("unit", map.get("unit"));
                            map2.put("time", map.get("time"));
                            if(d!=0){
                                map2.put("value",d+ snCodeRelation.getChangeData());
                            }else {
                                map2.put("value", map.get("value"));
                            }
                            targetList2.add(map2);
                        }
                        mapping2.put(key, targetList2);
                    }else {
                        mapping2.put(key, mapping.get(key));
                    }
                    log.info("targetList2:{}", JSONObject.toJSONString(targetList2));
                }else {
                    mapping2.put(key, mapping.get(key));
                }
            }
        }else {
            mapping2 = mapping;
        }

        // 处理为0的问题
        mapping2.forEach((s, mapList) -> {
            if (CollectionUtil.isNotEmpty(mapList)) {
                mapList.removeIf(map -> {
                    Double value = MapUtil.getDouble(map, "value", null);
                    return value == null || value < 0;
                    //return value == null || value <= 0;
                });
                //保留2位小数
                mapList.forEach(map->{
                    map.put("value", BigDecimal.valueOf(MapUtil.getDouble(map, "value")).setScale(2,BigDecimal.ROUND_HALF_UP));
                });
            }
        });
        return mapping2;
    }
    @Resource
    private IotService iotService;
    /**
     * 查询opentsdb的降精度数据
     */
    private Map<String, List<Map<String, Object>>> queryDownsampleByIotdb(OptDeviceHistoryDto historyDto) {
        Assert.notEmpty(historyDto.getAggregator(), "设备实时数据，聚合方式不允许为空");
        Assert.notEmpty(historyDto.getDownsample(), "设备实时数据，聚合时间不允许为空");
        Map<String, List<Map<String, Object>>> map = new HashMap<>();
        System.out.println(StrUtil.format("{}-{}", historyDto.getDownsample(), historyDto.getAggregator()));
        JSONObject object = iotService.aggregationQuery(historyDto);
        DateTimeFormatter dfDate = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        DateTimeFormatter dfTime = DateTimeFormatter.ofPattern("HH:mm");

        Constant.MonitorInfo monitorInfo = Constant.MonitorInfo.getInfoByCode(historyDto.getMonitorCode());
        String defaultUnit = monitorInfo == null ? null : monitorInfo.getUnit();

        for (Map.Entry<String, Object> entry : object.entrySet()) {
            String key = entry.getKey();
            JSONArray value = (JSONArray) entry.getValue();
            List<Map<String, Object>> v = new ArrayList<>();
            value.forEach(o -> {
                JSONObject oo = (JSONObject)o;
                Map<String, Object> object1 = new HashMap<>();
                long tiemstamp = oo.getLong("time");
                Instant instant=Instant.ofEpochMilli(tiemstamp);
                ZoneId zoneId = ZoneId.systemDefault();
                LocalDateTime localDateTime=instant.atZone(zoneId).toLocalDateTime();

                object1.put("date", dfDate.format(localDateTime));
                object1.put("unit", defaultUnit);
                object1.put("time", dfTime.format(localDateTime));
                object1.put("value", oo.getDoubleValue("val"));
                v.add(object1);
            });
            map.put(key, v);
        }
        return map;
    }

    /**
     * 获取程序分析的历史数据
     */
    private Map<String, List<Map<String, Object>>> getCustomHistory(OptDeviceHistoryDto historyDto) {
        Date startTime = historyDto.getStartTime();
        Date endTime = historyDto.getEndTime();
        // 1. 代码分析类的数据因为是按天分析的, 所以只能为24小时一组数据 解析开始到结束时间的间隔
        List<DateTime> dateRangeList = CollUtil.newArrayList((Iterable<DateTime>) new DateRange(startTime, endTime, DateField.DAY_OF_YEAR, 1));

        // 2. 查库
        LambdaQueryWrapper<OptDeviceMonitorAnalyseData> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.in(OptDeviceMonitorAnalyseData::getDeviceCode, historyDto.getDeviceCodes());
        queryWrapper.eq(OptDeviceMonitorAnalyseData::getCode, historyDto.getMonitorCode());
        queryWrapper.between(OptDeviceMonitorAnalyseData::getMonitorTime, startTime, endTime);
        List<OptDeviceMonitorAnalyseData> list = iOptDeviceMonitorAnalyseDataService.list();

        // 3. 循环所有查询的设备 进行组装数据 目的是不让前台查到的设备有缺失
        Map<String, List<Map<String, Object>>> map = new LinkedHashMap<>();
        for (String deviceCode : historyDto.getDeviceCodes()) {
            List<Map<String, Object>> list1 = new ArrayList<>();
            // 3.1 获取查询到的, 这个设备的所有数据(因为约定的是只传一个指标, 所以理论上不会有一个设备多个指标数据的情况)
            List<OptDeviceMonitorAnalyseData> monitorList = list.stream().filter(item -> StrUtil.equals(deviceCode, item.getDeviceCode())).collect(Collectors.toList());
            for (DateTime dateTime : dateRangeList) {
                // 3.1.1 获取当前时间节点的字符串时间
                String currentDateStr = dateTime.toDateStr();
                Map<String, Object> map2 = new HashMap<>();
                // 3.1.2 获取一个当前字符串时间的数值
                OptDeviceMonitorAnalyseData one = CollectionUtil.findOne(monitorList, data ->
                        StrUtil.equals(currentDateStr, DateUtil.formatDate(data.getMonitorTime()))
                );
                // 3.1.3 容错, 没有单位或者没有值的情况
                double value;
                String unit;
                if (one == null) {
                    value = 0D;
                    unit = Constant.MonitorInfo.getCustomInfoByCode(historyDto.getMonitorCode()).getUnit();
                } else {
                    // 注入值 3位小数
                    value = NumberUtil.round(one.getCodeValue(), 3).doubleValue();
                    unit = one.getDataUnit();
                }
                // 3.1.4 组装查询结果
                map2.put("value", value);
                map2.put("unit", unit);
                map2.put("date", DateUtil.formatDate(dateTime));
                map2.put("time", DateUtil.format(dateTime, "HH:mm"));
                list1.add(map2);
            }
            map.put(deviceCode, list1);
        }
        return map;
    }

    private List<DateTime> getDateRangeByDownsample(Date startTime, Date endTime, String dowsample) {
        List<DateTime> rangeList = new ArrayList<>();
        switch (dowsample) {
            case "15m": // 15分钟
                rangeList = CollUtil.newArrayList((Iterable<DateTime>) new DateRange(startTime, endTime, DateField.MINUTE, 15));
                break;
            case "30m": // 30分钟
                rangeList = CollUtil.newArrayList((Iterable<DateTime>) new DateRange(startTime, endTime, DateField.MINUTE, 30));
                break;
            case "1h": // 1小时
                rangeList = CollUtil.newArrayList((Iterable<DateTime>) new DateRange(startTime, endTime, DateField.HOUR, 1));
                break;
            case "3h": // 3小时
                rangeList = CollUtil.newArrayList((Iterable<DateTime>) new DateRange(startTime, endTime, DateField.HOUR, 3));
                break;
            case "6h": // 6小时
                rangeList = CollUtil.newArrayList((Iterable<DateTime>) new DateRange(startTime, endTime, DateField.HOUR, 6));
                break;
            case "12h": // 12小时
                rangeList = CollUtil.newArrayList((Iterable<DateTime>) new DateRange(startTime, endTime, DateField.HOUR, 12));
                break;
            case "24h": // 24小时
            case "1d": // 24小时
                rangeList = CollUtil.newArrayList((Iterable<DateTime>) new DateRange(startTime, endTime, DateField.DAY_OF_YEAR, 1));
                break;
            case "1w": // 一周
//                rangeList = CollUtil.newArrayList((Iterable<DateTime>) new DateRange(startTime, endTime, DateField.WEEK_OF_YEAR, 1));
                rangeList = CollUtil.newArrayList((Iterable<DateTime>) new DateRange(startTime, endTime, DateField.DAY_OF_YEAR, 1));
                break;
            case "1n": // 一月
//                rangeList = CollUtil.newArrayList((Iterable<DateTime>) new DateRange(startTime, endTime, DateField.MONTH, 1));
                rangeList = CollUtil.newArrayList((Iterable<DateTime>) new DateRange(startTime, endTime, DateField.DAY_OF_YEAR, 1));
                break;
            case "3n": // 一季度
//                rangeList = CollUtil.newArrayList((Iterable<DateTime>) new DateRange(startTime, endTime, DateField.MONTH, 3));
                rangeList = CollUtil.newArrayList((Iterable<DateTime>) new DateRange(startTime, endTime, DateField.DAY_OF_YEAR, 1));
                break;
            case "6n": // 半年
//                rangeList = CollUtil.newArrayList((Iterable<DateTime>) new DateRange(startTime, endTime, DateField.MONTH, 6));
                rangeList = CollUtil.newArrayList((Iterable<DateTime>) new DateRange(startTime, endTime, DateField.DAY_OF_YEAR, 1));
                break;
            case "1y": // 一年
//                rangeList = CollUtil.newArrayList((Iterable<DateTime>) new DateRange(startTime, endTime, DateField.YEAR, 1));
                rangeList = CollUtil.newArrayList((Iterable<DateTime>) new DateRange(startTime, endTime, DateField.DAY_OF_YEAR, 1));
                break;
            default:
                throw new ServiceException("时间采集周期不存在");
        }
        return rangeList;
    }

	private String getDateStrByDownsample(Date date, String dowsample) {
		if (dowsample.contains("m")) {
			return DateUtil.format(date, "yyyy-MM-dd HH:mm");
		} else if (dowsample.contains("h")) {
			return DateUtil.format(date, "yyyy-MM-dd HH");
		} else if (dowsample.contains("d") || "24h".equals(dowsample)) {
			return DateUtil.format(date, "yyyy-MM-dd");
		} else if (dowsample.contains("w")) {
			return DateUtil.format(date, "yyyy-MM-dd");
		} else if (dowsample.contains("n")) {
			return DateUtil.format(date, "yyyy-MM-dd");
		} else if (dowsample.contains("y")) {
			return DateUtil.format(date, "yyyy-MM-dd");
		} else {
			throw new ServiceException("时间采集周期不存在");
		}
	}

    public static void main(String[] args) throws ParseException {
    	
    	OpenTSDBUtils openTSDBUtils = new OpenTSDBUtils();
    	openTSDBUtils.setHost("http://112.102.225.89");
    	openTSDBUtils.setPort(44081);
    	openTSDBUtils.setMetric("monitor.equipment_tzsw");
    	openTSDBUtils.setTimeout(120);
    	
    	SimpleDateFormat sdf_date = new SimpleDateFormat("yyyy-MM-dd");
    	SimpleDateFormat sdf_datetime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    	Date startTime = sdf_datetime.parse("2023-05-01 00:00:00");
        Date endTime = sdf_datetime.parse("2023-06-30 23:59:59");
        System.out.println(startTime);
        System.out.println(endTime);
        // 1. 创建snCodeFilter和monitorCode过滤器
        SubQuery.Filter snCodeFilter = OpenTSDBUtils.createSnCodeFilter("2G19XA");
        SubQuery.Filter monitorCodeFilter = OpenTSDBUtils.createMonitorCodeFilter("0x0527");
        // 2. 计算时间
        long startDateTime = OpenTSDBUtils.serializedTime(startTime);
        long endDateTime = OpenTSDBUtils.serializedTime(endTime);

        //一个月
        String downsample = "1n-avg";
        System.out.println(downsample);
        // 3. 子查询
        SubQuery sub = SubQuery
                .metric(openTSDBUtils.getMetric())
                // 聚合方式
                .aggregator(SubQuery.Aggregator.NONE)
                .downsample(downsample)
                .filter(snCodeFilter)
                .filter(monitorCodeFilter)
                .build();
        // 4. 查询主体
        Query queryParams = Query
                // 开始时间 秒
                .begin(startDateTime)
                // 结束时间 秒
                .end(endDateTime)
                // 时区相关
                .useCalendar()
                .timezone(OpenTSDBUtils.OPENTSDB_TIMEZONE)
                // 子查询
                .sub(sub)
                .build();

        // 5. 进行查询
        OpenTSDBClient queryClient = null;
        try {
            // 5.1 创建连接
            queryClient  = openTSDBUtils.readClient();
            List<QueryResult> resultList = queryClient.query(queryParams);
            System.out.println(JSONObject.toJSONString(resultList));
            for (int i = 0; i < resultList.size(); i++) {
            	double avg = 0D;
            	int count = 0;
            	QueryResult result = resultList.get(i);
            	LinkedHashMap<Long, Number> map = result.getDps();
            	count = map.keySet().size();
            	for (Long time : map.keySet()) {
            		System.out.print(new Date(time*1000));
            		System.out.print("=");
            		System.out.println(map.get(time).doubleValue());
            		avg += map.get(time).doubleValue();
				}
            	System.out.println(avg);
            	System.out.println(count);
                System.out.println(avg/count);
			}
        } catch (IOReactorException e) {
            log.error("创建opentsd连接失败", e);
        } catch (IOException | ExecutionException | InterruptedException e) {
            log.error("查询opentsdb失败", e);
        } finally {
            try {
                if (queryClient != null) queryClient.gracefulClose();
            } catch (IOException e) {
                log.error("关闭opentsdb连接失败", e);
            }
        }

	}
}
